package luj.tool.jnproxyan.framework.lujnet.receive;

import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.socksx.v5.Socks5AuthMethod;
import luj.bean.api.BeanContext;
import luj.bean.api.bean.Bean;
import luj.net.api.server.ConnectionAcceptInitializer;
import luj.net.api.server.FrameDataReceiver;
import luj.tool.jnproxyan.framework.lujnet.ConnState;
import luj.tool.jnproxyan.proxy.network.socks.actor.root.message.request.init.refuse33.ReqDeny33AuthMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.LinkedHashSet;
import java.util.Set;

@Component
final class Recv33Methods implements FrameDataReceiver {

  // https://www.ietf.org/rfc/rfc1928.txt
  // 6.  Replies
  @Override
  public Result receive(Context ctx) {
    ConnState state = ctx.getConnectionState();
    ByteBuf frameBuf = ctx.getLastFrame();
    int nMethods = state.getnMethods();

    Set<Socks5AuthMethod> methodSet = new LinkedHashSet<>(nMethods);
    state.setMethods(methodSet);
    for (int i = 0; i < nMethods; i++) {
      byte method = frameBuf.readByte();
      methodSet.add(Socks5AuthMethod.valueOf(method));
    }

//    LOG.debug("method: {}", methodSet);
    ConnectionAcceptInitializer.Connection conn = state.getC2pConnection();

    //TODO: 回复客户端MethodSelectionResponseMessage
    //TODO: Recv41RequestHeader
    ByteBuf buf = conn.alloc().buffer();
    buf.writeByte(0x05); // VER

    if (!methodSet.contains(NO_AUTH)) {
      LOG.debug("拒绝 -- {}", methodSet);

      // X'FF' NO ACCEPTABLE METHODS
      conn.write(buf.writeByte(0xFF));
      conn.flush();
      conn.close();

      updateReqStatus(state);
      return ctx.then()
          .waitBytes(Integer.MAX_VALUE)
          .nextReceiver(this);
    }

    conn.write(buf.writeByte(NO_AUTH));
    conn.flush();

    return ctx.then()
        .waitBytes(4)
        .nextReceiver(_receiveRequest);
  }

  private void updateReqStatus(ConnState state) {
    Bean<ReqDeny33AuthMsg> msg = _lujbean.createBean(ReqDeny33AuthMsg.class);
    msg.setField(m -> m::reqId, state.getReqId());
    msg.setField(m -> m::version, state.getVer());
    msg.setField(m -> m::methods, state.getMethods());

    state.getSocksRef().tell(msg.getValueInstance());
  }

  private static final Logger LOG = LoggerFactory.getLogger(Recv33Methods.class);

  // X'00' NO AUTHENTICATION REQUIRED
  private static final Integer NO_AUTH = 0x00;

  @Autowired
  Recv41RequestHeader _receiveRequest;

  @Autowired
  BeanContext _lujbean;
}
